﻿using System;
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using static UnityEngine.ParticleSystem;
namespace SplitMesh
{
    /// <summary>
    /// 模型切割脚本
    /// </summary>
    [RequireComponent(typeof(MeshFilter))]
    [RequireComponent(typeof(MeshCollider))]
    public class SplitObject1 : MonoBehaviour
    {
        [SerializeField] GameObject mPrefab;
        public Transform[] tfs;
        public Transform dot0;
        public Transform dot1;

        Vector3 from, to, size;

        public MeshInfo MeshInfo { get; set; }
        List<Vector3> vs = new List<Vector3>();


        Ray edgeRay;
        List<Plane> mPlanes = new List<Plane>();

        private void Update()
        {
            for (int i = 0; i < vs.Count - 1; i++)
            {
                Debug.DrawLine(vs[i], vs[i+1], Color.blue);
            }
            Debug.DrawLine(vs[vs.Count-1], vs[0], Color.blue);
        }

        void Start()
        {
            edgeRay = new Ray();

            for (int j = 0; j < tfs.Length; j++)
            {
                vs.Add(tfs[j].position);
            }
            for (int i = 0; i < vs.Count-1; i++)
            {
                var plane = new Plane(Vector3.Cross(vs[i + 1] - vs[i], Vector3.up).normalized, vs[i + 1]);
                mPlanes.Add(plane);
            }
            var plane_temp = new Plane(Vector3.Cross(vs[0] - vs[vs.Count-1], Vector3.up).normalized, vs[0]);
            mPlanes.Add(plane_temp);

            if (MeshInfo == null)
            {
                MeshInfo = new MeshInfo(mPrefab.GetComponent<MeshFilter>().sharedMesh);
            }
            Split();

            mPrefab.gameObject.SetActive(false);

        }
        public void UpdateMesh(GameObject go, params MeshInfo[] info)
        {
            CombineInstance[] coms = new CombineInstance[info.Length];
            for (int i = 0; i < info.Length; i++)
            {
                coms[i].mesh = info[i].GetMesh();
                coms[i].transform = Matrix4x4.identity;
            }
            Mesh mesh = new Mesh();
            mesh.CombineMeshes(coms);
            mesh.RecalculateBounds();
            //mesh.RecalculateNormals();
            go.GetComponent<MeshFilter>().mesh = mesh;
            go.GetComponent<MeshCollider>().sharedMesh = mesh;
            MeshInfo = new MeshInfo(mesh);
            MeshInfo.center = info[0].center;
            MeshInfo.size = info[0].size;
        }


        public MeshInfo Split()
        {
            //Vector3 point = transform.InverseTransformPoint(plane.normal * -plane.distance);
            //Vector3 normal = transform.InverseTransformDirection(plane.normal);
            //normal.Scale(transform.localScale);
            //normal.Normalize();


            MeshInfo a = new MeshInfo();
            //MeshInfo b = new MeshInfo();
            //
            bool[] above = new bool[MeshInfo.vertices.Count];
            int[] newTriangles = new int[MeshInfo.vertices.Count];

            for (int i = 0; i < newTriangles.Length; i++)
            {
                Vector3 vert = MeshInfo.vertices[i];
                above[i] = MathTool.IsPointInPolygon(vert, vs);
                if (above[i])
                {
                    newTriangles[i] = a.vertices.Count;
                    Debug.LogError(i);
                    a.Add(vert, MeshInfo.uvs[i], MeshInfo.normals[i], MeshInfo.tangents[i]);
                }
                //else
                //{
                //    newTriangles[i] = b.vertices.Count;
                //    b.Add(vert, MeshInfo.uvs[i], MeshInfo.normals[i], MeshInfo.tangents[i]);
                //}
            }

            int triangleCount = MeshInfo.triangles.Count / 3;
            for (int i = 0; i < triangleCount; i++)
            {
                int _i0 = MeshInfo.triangles[i * 3];
                int _i1 = MeshInfo.triangles[i * 3 + 1];
                int _i2 = MeshInfo.triangles[i * 3 + 2];

                bool _a0 = above[_i0];
                bool _a1 = above[_i1];
                bool _a2 = above[_i2];
                // 如果三角形的三个点都在多边形内部，那么直接加入
                if (_a0 && _a1 && _a2)
                {
                    a.triangles.Add(newTriangles[_i0]);
                    a.triangles.Add(newTriangles[_i1]);
                    a.triangles.Add(newTriangles[_i2]);
                }
                else if (!_a0 && !_a1 && !_a2)
                {
                    //b.triangles.Add(newTriangles[_i0]);
                    //b.triangles.Add(newTriangles[_i1]);
                    //b.triangles.Add(newTriangles[_i2]);
                }
                else
                {
                    int up, down0, down1;
                    if (_a1 == _a2 && _a0 != _a1)
                    {
                        up = _i0;
                        down0 = _i1;
                        down1 = _i2;
                    }
                    else if (_a2 == _a0 && _a1 != _a2)
                    {
                        up = _i1;
                        down0 = _i2;
                        down1 = _i0;
                    }
                    else
                    {
                        up = _i2;
                        down0 = _i0;
                        down1 = _i1;
                    }

                    Vector3 v0 = MeshInfo.vertices[up];
                    Vector3 v1 = MeshInfo.vertices[down0];
                    Vector3 v2 = MeshInfo.vertices[down1];
                    Vector2 u0 = MeshInfo.uvs[up];
                    Vector2 u1 = MeshInfo.uvs[down0];
                    Vector2 u2 = MeshInfo.uvs[down1];


                    var normal = MeshInfo.normals[up];
                    var tan = MeshInfo.tangents[up];
                    if (above[up])
                    {
                        Vector3 pos_a = Vector3.zero;
                        Vector2 uv_a = Vector2.zero;
                        MeshTool.GetPos(mPlanes, v1, v0, edgeRay, u0, u2, out pos_a, out uv_a);
                        //Debug.DrawLine(Vector3.zero, pos_a,Color.cyan, 60*60);
                        //Debug.DrawLine(v1, v1 + (v0 - v1) * 0.9f, Color.red, 60 * 60);
                        //Debug.DrawLine(v1, v1 + (v2-v1)*0.9f, Color.green, 60 * 60);

                        Vector3 pos_b = Vector3.zero;
                        Vector2 uv_b = Vector2.zero;
                        MeshTool.GetPos(mPlanes,v2,v0, edgeRay,u0,u2, out pos_b, out uv_b);
                        //Debug.DrawLine(v2, pos_b, Color.green, 60 * 60);
                        //Debug.DrawLine(Vector3.zero, pos_b, Color.black, 60 * 60);

                        var top = a;
                        int top_a = top.vertices.Count;
                        top.Add(pos_a, uv_a, normal, tan);
                        int top_b = top.vertices.Count;
                        top.Add(pos_b, uv_b, normal, tan);

                        var top_up = newTriangles[up];
                        top.triangles.Add(top_up);
                        top.triangles.Add(top_a);
                        top.triangles.Add(top_b);


                    }
                    else
                    {
                        Vector3 pos_a = Vector3.zero;
                        Vector2 uv_a = Vector2.zero;
                        MeshTool.GetPos(mPlanes, v0, v1, edgeRay, u0, u1, out pos_a, out uv_a);


                        Vector3 pos_b = Vector3.zero;
                        Vector2 uv_b = Vector2.zero;
                        MeshTool.GetPos(mPlanes, v0, v2, edgeRay, u0, u2, out pos_b, out uv_b);
                        //Debug.DrawLine(Vector3.zero, pos_b, Color.black, 60 * 60);



                        //Debug.DrawLine(Vector3.zero, pos_a, Color.cyan, 60 * 60);
                        ////Debug.DrawLine(v1, v1 + (v2 - v1) * 0.9f, Color.green, 60 * 60);
                        //Debug.DrawLine(v0, v0 + (v2 - v0) * 0.9f, Color.green, 60 * 60);
                        //Debug.DrawLine(v0, v0 + (v1 - v0) * 0.7f, Color.red, 60 * 60);



                        var top = a;
                        int top_a = top.vertices.Count;
                        top.Add(pos_a, uv_a, normal, tan);
                        int top_b = top.vertices.Count;
                        top.Add(pos_b, uv_b, normal, tan);
                        //top.triangles.Add(newTriangles[up]);
                        //top.triangles.Add(top_a);
                        //top.triangles.Add(top_b);

                        //top.triangles.Add(newTriangles[up]);
                        //top.triangles.Add(top_a);
                        //top.triangles.Add(top_b);



                        top.triangles.Add(newTriangles[down0]);
                        top.triangles.Add(newTriangles[down1]);
                        top.triangles.Add(top_b);

                        top.triangles.Add(newTriangles[down0]);
                        top.triangles.Add(top_b);
                        top.triangles.Add(top_a);
                    }

                    //if (above[up])
                    //    SplitTriangle(a, null, newTriangles, up, down0, down1, out pos0, out pos1);
                    //else
                    //    SplitTriangle(b, a, point, normal, newTriangles, up, down0, down1, out pos1, out pos0);
                    //cutPoint.Add(pos0);
                    //cutPoint.Add(pos1);
                }
            }

            a.CombineVertices(0.001f);
            a.center = MeshInfo.center;
            a.size = MeshInfo.size;
            //b.CombineVertices(0.001f);
            //b.center = MeshInfo.center;
            //b.size = MeshInfo.size;

            var go = Instantiate(mPrefab);
            UpdateMesh(go,a);

            //Instantiate(gameObject).GetComponent<SplitObject>().UpdateMesh(b);
            //Destroy(gameObject);
            return a;
        }
        public ValueTuple<Vector3, Vector2> Intersect(Plane plane, Vector3 first, Vector3 second, Vector2 uv1, Vector2 uv2)
        {
            edgeRay.origin = first;
            edgeRay.direction = (second - first).normalized;
            float dist;
            float maxDist = Vector3.Distance(first, second);

            if (!plane.Raycast(edgeRay, out dist))
                // Intersect in wrong direction...
                throw new UnityException("Line-Plane intersect in wrong direction");
            else if (dist > maxDist)
                // Intersect outside of line segment
                throw new UnityException("Intersect outside of line");

            var returnVal = new ValueTuple<Vector3, Vector2>
            {
                Item1 = edgeRay.GetPoint(dist)
            };

            var relativeDist = dist / maxDist;
            // Compute new uv by doing Linear interpolation between uv1 and uv2
            returnVal.Item2.x = Mathf.Lerp(uv1.x, uv2.x, relativeDist);
            returnVal.Item2.y = Mathf.Lerp(uv1.y, uv2.y, relativeDist);
            return returnVal;
        }

        void SplitTriangle(MeshInfo top, MeshInfo bottom, Vector3 point, Vector3 normal, int[] newTriangles, int up, int down0, int down1)
        {
            //Vector3 v0 = MeshInfo.vertices[up];
            //Vector3 v1 = MeshInfo.vertices[down0];
            //Vector3 v2 = MeshInfo.vertices[down1];
            //float topDot = Vector3.Dot(point - v0, normal);
            //float aScale = Mathf.Clamp01(topDot / Vector3.Dot(v1 - v0, normal));
            //float bScale = Mathf.Clamp01(topDot / Vector3.Dot(v2 - v0, normal));
            //Vector3 pos_a = v0 + (v1 - v0) * aScale;
            //Vector3 pos_b = v0 + (v2 - v0) * bScale;

            //Vector2 u0 = MeshInfo.uvs[up];
            //Vector2 u1 = MeshInfo.uvs[down0];
            //Vector2 u2 = MeshInfo.uvs[down1];
            //Vector3 uv_a = (u0 + (u1 - u0) * aScale);
            //Vector3 uv_b = (u0 + (u2 - u0) * bScale);

            //Vector3 n0 = MeshInfo.normals[up];
            //Vector3 n1 = MeshInfo.normals[down0];
            //Vector3 n2 = MeshInfo.normals[down1];
            //Vector3 normal_a = (n0 + (n1 - n0) * aScale).normalized;
            //Vector3 normal_b = (n0 + (n2 - n0) * bScale).normalized;

            //Vector4 t0 = MeshInfo.tangents[up];
            //Vector4 t1 = MeshInfo.tangents[down0];
            //Vector4 t2 = MeshInfo.tangents[down1];
            //Vector4 tangent_a = (t0 + (t1 - t0) * aScale).normalized;
            //Vector4 tangent_b = (t0 + (t2 - t0) * bScale).normalized;
            //tangent_a.w = t1.w;
            //tangent_b.w = t2.w;

            //int top_a = top.vertices.Count;
            //top.Add(pos_a, uv_a, normal_a, tangent_a);
            //int top_b = top.vertices.Count;
            //top.Add(pos_b, uv_b, normal_b, tangent_b);
            //top.triangles.Add(newTriangles[up]);
            //top.triangles.Add(top_a);
            //top.triangles.Add(top_b);




            //int down_a = bottom.vertices.Count;
            //bottom.Add(pos_a, uv_a, normal_a, tangent_a);
            //int down_b = bottom.vertices.Count;
            //bottom.Add(pos_b, uv_b, normal_b, tangent_b);

            //bottom.triangles.Add(newTriangles[down0]);
            //bottom.triangles.Add(newTriangles[down1]);
            //bottom.triangles.Add(down_b);

            //bottom.triangles.Add(newTriangles[down0]);
            //bottom.triangles.Add(down_b);
            //bottom.triangles.Add(down_a);

            //pos0 = pos_a;
            //pos1 = pos_b;
        }

//        void Update()
//        {
//#if UNITY_STANDALONE_WIN || UNITY_EDITOR || UNITY_WEBGL
//            if (Input.GetMouseButtonDown(0))
//            {
//                from = Input.mousePosition;
//            }
//            if (Input.GetMouseButton(0))
//            {
//                to = Input.mousePosition;
//            }
//            if (Input.GetMouseButtonUp(0))
//            {
//                Debug.LogError(from);
//                Debug.LogError(to);


//                size = new Vector2(Screen.width, Screen.height);
//                Plane plane;
//                var cam = Camera.main;
//                float near = cam.nearClipPlane;
//                Vector3 line = from - to;
//                float rayLength = new Vector2(line.x / size.x, line.y / size.y).magnitude;
//                line = cam.ScreenToWorldPoint(new Vector3(this.to.x, this.to.y, near)) - cam.ScreenToWorldPoint(new Vector3(this.from.x, this.from.y, near));
//                Ray ray = cam.ScreenPointToRay(to);
//                RaycastHit hit;
//                if (Physics.Raycast(ray, out hit))
//                {
//                    plane = new Plane(Vector3.Cross(line, ray.direction).normalized, hit.point);
//                    Vector3 point = transform.InverseTransformPoint(plane.normal * -plane.distance);
//                    Debug.LogError(plane.normal);
//                    Debug.LogError(plane.distance);
//                    Debug.LogError(plane.normal * -plane.distance);
//                    Debug.LogError(point);
//                    Vector3 normal = transform.InverseTransformDirection(plane.normal);
//                    Debug.LogError(normal);


//                    DrawPlane(hit.point, plane.normal);
//                }
//            }
//#elif UNITY_ANDROID || UNITY_IPHONE
//            for (int i = 0; i < Input.touchCount; i++)
//            {
//                if(Input.GetTouch(i).phase== TouchPhase.Began)
//                    from = Input.mousePosition;
//                if(Input.GetTouch(i).phase == TouchPhase.Moved)
//                    to = Input.mousePosition;
//                if (Input.GetTouch(i).phase == TouchPhase.Ended)
//                    SetSplitObject(from, to);
                
//            }
//#endif
//        }
//        public GameObject plane;
//        void DrawPlane(Vector3 start, Vector3 normalVec)
//        {
//            Quaternion rotate = Quaternion.FromToRotation(Vector3.up, normalVec);

//            plane.transform.localRotation = rotate;
//            plane.transform.position = start;
//            plane.SetActive(true);
//        }
//        void OnPostRender()
//        {
//            GLLine(from, to);
//        }
//        void GLLine(Vector2 from, Vector2 to)
//        {
//            size = new Vector2(Screen.width, Screen.height);
//            GL.PushMatrix();
//            GL.LoadOrtho();
//            GL.Begin(GL.LINES);
//            GL.Color(Color.green);
//            GL.Vertex3(from.x / size.x, from.y / size.y, Camera.main.nearClipPlane);
//            GL.Vertex3(to.x / size.x, to.y / size.y, Camera.main.nearClipPlane);
//            GL.End();
//            GL.PopMatrix();
//        }


    }
}